home *** CD-ROM | disk | FTP | other *** search
/ Aminet 51 / Aminet 51 (2002)(GTI - Schatztruhe)[!][Oct 2002].iso / Aminet / dev / gg / tcpbug.lha / tcpbug / relay.c < prev    next >
Encoding:
C/C++ Source or Header  |  1998-04-14  |  4.4 KB  |  176 lines

  1. #include <errno.h>
  2. #include <string.h>
  3. #include <unistd.h>
  4. #include <sys/stat.h>
  5. #include <sys/time.h>
  6. #include <sys/types.h>
  7. #if defined(__hpux__) || defined(__linux__)
  8. #  include <sys/ioctl.h>
  9. #else
  10. #  include <sys/filio.h>
  11. #endif
  12. #include <sys/socket.h>
  13.  
  14. #include "relay.h"
  15.  
  16.  
  17. #define Export
  18.  
  19.  
  20. #define MIN(a,b)    ((a) < (b) ? (a) : (b))
  21. #define MAX(a,b)    ((a) > (b) ? (a) : (b))
  22.  
  23.  
  24. /*
  25.  *  Estimate how many bytes are available for reading on a file descriptor.
  26.  */
  27. static    long
  28. available_bytes(int fd)
  29. {
  30.     long      nbytes;
  31.     struct stat      sb;
  32.     off_t      pos;
  33.  
  34.     /*
  35.      *  Some Unices don't support the FIONREAD ioctl on regular files.
  36.      *  They don't even return an error, but says there are zero bytes
  37.      *  to be read.  We then try to fstat() the file descriptor to see
  38.      *  how big the file is, and try to determine at what offset the
  39.      *  file pointer is at now.  That doesn't work always either...
  40.      */
  41.  
  42.     if (ioctl(fd, FIONREAD, &nbytes) < 0  ||  nbytes == 0)
  43.     {
  44.     if (fstat(fd, &sb) < 0)
  45.         return -1;
  46.     errno = 0;
  47.     pos = lseek(fd, 0, SEEK_CUR);
  48.     if ((pos < 0  &&  errno != 0)  ||  !S_ISREG(sb.st_mode))
  49.         pos = 0;
  50.     nbytes = sb.st_size - pos;
  51.     }
  52.     return nbytes;
  53. }
  54.  
  55.  
  56.  
  57. /*
  58.  *  Wait for data to arrive on any of the source file descriptors in
  59.  *  RELAYS, read all available data, and write to respective dest
  60.  *  file descriptors.  TIMEOUT is the maximum time to wait, or a nil
  61.  *  pointer to never time out.  For each read() done, the function
  62.  *  CALLBACK is called.  Note that CALLBACK may be called more than
  63.  *  once for each file descriptor if much data is available.
  64.  *  NRELAYS is the number of elements in RELAYS.
  65.  *
  66.  *  If the 'readerror' field is non-zero for a relay, no read will be
  67.  *  attempted from the source file descriptor of that relay.
  68.  *  If the 'writerror' field is non-zero for a relay, no write will be
  69.  *  attempted to the dest file descriptor of that relay, but the source
  70.  *  descriptor will still be drained from any available data.
  71.  *
  72.  *  If end-of-file is reached on the source of a relay, the corresponding
  73.  *  destination will be shutdown() for writing, thus causing the other
  74.  *  end to see a end-of-file.
  75.  *  If a write error occurs on the destination of a relay, the source
  76.  *  of that relay will be shutdown() for reading, causing the other end
  77.  *  to get errors when trying to write more data to us.
  78.  *  Note that the SIGPIPE signal should be ignored, or possibly caught,
  79.  *  by the caller, or the process will die when a receiver closes its
  80.  *  end for receiving.
  81.  *
  82.  *  Returns the number of file descriptor read from, or negative on error.
  83.  */
  84. Export    int
  85. relay_once(struct relay        * relays,
  86.        int              nrelays,
  87.        struct timeval    * timeout,
  88.        int (*callback)(struct relay*, char*, size_t)
  89.     )
  90. {
  91.     int        maxfd;
  92.     fd_set    readset;
  93.     int        i;
  94.     int        nfds;
  95.     int        nerrors        = 0;
  96.  
  97.     FD_ZERO(&readset);
  98.     maxfd = 0;
  99.     for (i = nrelays - 1 ;  i >= 0 ;  i--) 
  100.     {
  101.     if (!relays[i].readerror) {
  102.         FD_SET(relays[i].source, &readset);
  103.         if (relays[i].source > maxfd)
  104.         maxfd = relays[i].source;
  105.     }
  106.     }
  107.  
  108.     nfds = select(maxfd+1, &readset, (fd_set*)NULL, (fd_set*)NULL, timeout);
  109.     if (nfds <= 0) {
  110.     if (errno != EINTR)
  111.         return nfds;
  112.     else
  113.         return 0;
  114.     }
  115.  
  116.     for (i = nrelays - 1 ;  i >= 0 ;  i--)
  117.     {
  118.     if (FD_ISSET(relays[i].source, &readset))
  119.     {
  120.         long  unread = available_bytes(relays[i].source);
  121.         do 
  122.         {
  123.         char buffer[8192];
  124.         int bytes_read = read(relays[i].source, buffer, sizeof buffer);
  125.  
  126.         if (bytes_read < 0) {
  127.             relays[i].readerror = errno;
  128.             nerrors++;
  129.             break;
  130.         }
  131.         unread -= bytes_read;
  132.         if (bytes_read == 0) {
  133.             relays[i].readerror = -1;
  134.             shutdown(relays[i].dest, 1);
  135.             nerrors++;
  136.         }
  137.         if (callback)
  138.             (*callback)(&relays[i], buffer, bytes_read);
  139.         if (!relays[i].writeerror  &&  bytes_read > 0) {
  140.             int written = write(relays[i].dest, buffer, bytes_read);
  141.             if (written < 0) {
  142.             relays[i].writeerror = errno;
  143.             shutdown(relays[i].source, 0);
  144.             nerrors++;
  145.             break;
  146.             }
  147.         }
  148.         } while (unread > 0);
  149.     }
  150.     }
  151.  
  152.     return nerrors ? -nerrors : nfds;
  153. }
  154.  
  155.  
  156.  
  157. /*
  158.  *  Call relay_once() until end-of-file has been reached on all sources.
  159.  *  No time limit.
  160.  */
  161. Export    int
  162. relay_all(struct relay    * relaylist,
  163.       int          nrelays,
  164.       int (*callback)(struct relay*, char*, size_t))
  165. {
  166.     int  nclosed = 0;
  167.     do
  168.     {
  169.     int  status = relay_once(relaylist, nrelays, NULL, callback);
  170.     if (status < 0)
  171.         nclosed += -status;
  172.     } while (nclosed < nrelays);
  173.  
  174.     return 0;
  175. }
  176.